#include "dialognn.h"
#include "ui_dialognn.h"
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QSettings>
#include <QThread>
#include <math.h>
DialogNN::DialogNN(QWidget *parent) :
	QDialog(parent),
	ui(new Ui::DialogNN),
	m_process(new QProcess(this)),
	m_processBoard(new QProcess(this)),
	m_nTimer(-1)
{
	ui->setupUi(this);
	Qt::WindowFlags flg = windowFlags();
	flg |= Qt::WindowMinMaxButtonsHint;
	setWindowFlags(flg);
	ui->pushButton_stop->setDisabled(true);
	ui->pushButton_trainning->setDisabled(true);
	loadIni();

	connect (m_process, &QProcess::readyReadStandardError,this,&DialogNN::slot_readyReadOutput);
	connect (m_process, &QProcess::readyReadStandardOutput,this,&DialogNN::slot_readyReadErr);
#if QT_VERSION >=0x060000
	connect (m_process, &QProcess::finished,this,&DialogNN::slot_finished);
#else
	connect (m_process, static_cast<void (QProcess::*)(int)>(&QProcess::finished),this,&DialogNN::slot_finished);
#endif
	connect (m_process, &QProcess::started,this,&DialogNN::slot_started);

	connect (m_processBoard, &QProcess::readyReadStandardError,this,&DialogNN::board_readyReadOutput);
	connect (m_processBoard, &QProcess::readyReadStandardOutput,this,&DialogNN::board_readyReadErr);
#if QT_VERSION >=0x060000
	connect (m_processBoard, &QProcess::finished,this,&DialogNN::board_finished);
#else
	connect (m_processBoard, static_cast<void (QProcess::*)(int)>(&QProcess::finished),this,&DialogNN::board_finished);
#endif
	connect (m_processBoard, &QProcess::started,this,&DialogNN::board_started);

	m_nTimer = startTimer(1000);

}

DialogNN::~DialogNN()
{
	m_process->kill();
	m_process->terminate();
	m_process->kill();
	m_processBoard->terminate();
	delete ui;
}

void DialogNN::on_toolButton_br_proj_clicked()
{
	QString strFileName = getFilePath(tr("Switch Project"),"lastProjectDir","Project files (*.qnprj *.cfg);;All files (*.*)",false);
	QFile fp(strFileName);
	fp.open(QIODevice::Append);
	fp.close();
	ui->lineEdit_projpath->setText(strFileName);
	saveIni();
}


void DialogNN::on_toolButton_br_NN_clicked()
{
	QString strFileName = QFileDialog::getExistingDirectory(this,tr("Features Directory"),ui->lineEdit_NNPath->text(),QFileDialog::ShowDirsOnly);
	ui->lineEdit_NNPath->setText(strFileName);
	saveIni();
}



void DialogNN::on_toolButton_br_features_clicked()
{
	QString strFileName = getFilePath(tr("Features File"),"lastFeatureDir","*.txt(*.txt);;All files (*.*)",true);
	ui->lineEdit_features->setText(strFileName);
	saveIni();
}

void DialogNN::on_pushButton_trainning_clicked()
{
	saveIni();
	savePrj();
	QString cmdline;

	if (ui->radioButton_train->isChecked())
	{
		QString cmd = "python.exe";
		cmdline += cmd;
		QStringList args;
		args << ui->lineEdit_NNPath->text() +"/gnn_training.py";
		args << "-t";
		args << ui->lineEdit_projpath->text();
		args << "-i";
		args << ui->lineEdit_features->text();
		QFileInfo info(ui->lineEdit_projpath->text());
		QString ModelPath = info.absolutePath() + "/" + info.completeBaseName() + "_Saved/Model";
		QString SummaryPath = info.absolutePath() + "/" + info.completeBaseName() + "_Saved/Summary";
		args << "-m";
		args << ModelPath;
		args << "-s";
		args << SummaryPath;
		QDir dir_model(ModelPath);
		QFileInfoList lstF= dir_model.entryInfoList(QDir::Files);
		if (lstF.isEmpty())
			ui->checkBox_firstTrainning->setChecked(true);
		if (ui->checkBox_firstTrainning->isChecked())
		{
			args << "-f";

			dir_model.removeRecursively();
			QDir dir_summary(SummaryPath);
			dir_summary.removeRecursively();
			dir_model.mkpath(ModelPath);
			dir_summary.mkpath(SummaryPath);
		}
		if (m_process->state()==QProcess::NotRunning)
		{
			m_process->setProgram(cmd);
			m_process->setArguments(args);
			foreach (QString c, args)
				cmdline += " " + c;
			ui->plainTextEdit_output->appendPlainText(cmdline+"\n");
			m_process->start();
			ui->checkBox_firstTrainning->setChecked(false);
			saveIni();
		}
	}
	else if (ui->radioButton_testing->isChecked())
	{
		QString cmd = "python.exe";
		cmdline += cmd;
		QStringList args;
		args << ui->lineEdit_NNPath->text() +"/gnn_test_model.py";
		args << "-t";
		args << ui->lineEdit_projpath->text();
		args << "-i";
		args << ui->lineEdit_features->text();
		QFileInfo info(ui->lineEdit_projpath->text());
		QString ModelPath = info.absolutePath() + "/" + info.completeBaseName() + "_Saved/Model";
		args << "-m";
		args << ModelPath;
		if (m_process->state()==QProcess::NotRunning)
		{
			m_process->setProgram(cmd);
			m_process->setArguments(args);
			foreach (QString c, args)
				cmdline += " " + c;
			ui->plainTextEdit_output->appendPlainText(cmdline+"\n");
			m_process->start();
		}
	}
	else
	{
		QString cmd = "python.exe";
		cmdline += cmd;
		QStringList args;
		args << ui->lineEdit_NNPath->text() +"/gnn_run_model.py";
		args << "-t";
		args << ui->lineEdit_projpath->text();
		args << "-i";
		args << ui->lineEdit_features->text();
		QFileInfo info(ui->lineEdit_projpath->text());
		QString ModelPath = info.absolutePath() + "/" + info.completeBaseName() + "_Saved/Model";
		args << "-m";
		args << ModelPath;
		QString Output = ui->lineEdit_features->text() + ".result";
		args << "-o";
		args << Output;
		if (m_process->state()==QProcess::NotRunning)
		{
			m_process->setProgram(cmd);
			m_process->setArguments(args);
			foreach (QString c, args)
			{
				if (c.startsWith("-"))
					cmdline += " " + c;
				else
					cmdline += " \"" + c + "\"";
			}
			ui->plainTextEdit_output->appendPlainText(cmdline+"\n");
			m_process->start();
		}
	}
}

void DialogNN::on_pushButton_stop_clicked()
{
	if (m_process->state()==QProcess::Running)
	{
		m_process->terminate();
		m_process->kill();
	}
}

void DialogNN::slot_readyReadOutput()
{
	ui->plainTextEdit_output->appendPlainText(
				QString::fromStdString(m_process->readAllStandardOutput().toStdString())
				);

}
void DialogNN::slot_readyReadErr()
{
	ui->plainTextEdit_output_err->appendPlainText(
				QString::fromStdString(m_process->readAllStandardError().toStdString())
				);
}
void DialogNN::slot_finished(int code)
{
	ui->groupBox_paras->setEnabled(true);
	ui->groupBox_task->setEnabled(true);
	ui->lineEdit_projpath->setEnabled(true);
	ui->lineEdit_features->setEnabled(true);
	ui->pushButton_trainning->setEnabled(true);
	ui->pushButton_stop->setDisabled(true);
	slot_readyReadOutput();
	slot_readyReadErr();
	ui->plainTextEdit_output->appendPlainText(QString("\nprogram returns %1\n")
											  .arg(code)
											  );

}
void DialogNN::slot_started()
{
	ui->groupBox_paras->setDisabled(true);
	ui->groupBox_task->setDisabled(true);
	ui->lineEdit_projpath->setDisabled(true);
	ui->lineEdit_features->setDisabled(true);
	ui->pushButton_trainning->setDisabled(true);
	ui->pushButton_stop->setEnabled(true);

	if (ui->radioButton_train->isChecked())
	{
		if (m_processBoard->state()==QProcess::Running)
		{
			m_processBoard->terminate();
			m_processBoard->kill();
			m_processBoard->waitForFinished();
			//++m_nPort;

		}

		for (int i=0;i<10;++i)
		{
			QThread::msleep(500);
			QCoreApplication::processEvents();
		}
		QFileInfo info(ui->lineEdit_projpath->text());
		QString SummaryPath = info.absolutePath() + "/" + info.completeBaseName() + "_Saved/Summary/";
		QString cmdline;
		QString cmd = "tensorboard";
		cmdline += cmd;
		QStringList args;
		args << "--logdir";
		args << SummaryPath;
		args << "--port";
		args << QString("%1").arg(m_nPort);
		args << "--host";
		args << "0.0.0.0";
		if (m_processBoard->state()==QProcess::NotRunning)
		{
			m_processBoard->setProgram(cmd);
			m_processBoard->setArguments(args);
			foreach (QString c, args)
				cmdline += " " + c;
			ui->plainTextEdit_log_out->appendPlainText(cmdline+"\n");
			m_processBoard->start();
		}
	}


}
void DialogNN::timerEvent(QTimerEvent * evt)
{
	if(m_nTimer==evt->timerId())
	{
		if (m_process->canReadLine())
		{
			slot_readyReadErr();
			slot_readyReadOutput();
		}
		if (m_processBoard->canReadLine())
		{
			board_readyReadErr();
			board_readyReadOutput();
		}
	}
}

QString DialogNN::getFilePath(QString title,QString prefix, QString filter, bool bOpen)
{
	QSettings settings(QCoreApplication::applicationFilePath()+".ini",QSettings::IniFormat);
	QString lastFolder = settings.value(QString("history/%1").arg(prefix),"./").toString();
	QString fileName;
	if (bOpen==false)
		fileName = QFileDialog::getSaveFileName(this,title,lastFolder,filter);
	else
		fileName = QFileDialog::getOpenFileName(this,title,lastFolder,filter);
	if (fileName.size())
	{
		QFileInfo info(fileName);
		settings.setValue(QString("history/%1").arg(prefix),info.absolutePath());
	}
	return fileName;
}
void DialogNN::saveIni()
{
	QSettings settings(QCoreApplication::applicationFilePath()+".ini",QSettings::IniFormat);
	settings.setValue("ui/lineEdit_projpath",ui->lineEdit_projpath->text());
	settings.setValue("ui/lineEdit_NNPath",ui->lineEdit_NNPath->text());
	settings.setValue("ui/lineEdit_features",ui->lineEdit_features->text());
	//current Project default
	settings.setValue("ui/lineEdit_layers",ui->lineEdit_layers->text());
	settings.setValue("ui/spinBox_inputs",ui->spinBox_inputs->value());
	settings.setValue("ui/spinBox_outputs",ui->spinBox_outputs->value());
	settings.setValue("ui/spinBox_regular_base",ui->spinBox_regular_base->value());
	settings.setValue("ui/spinBox_regular_exp",ui->spinBox_regular_exp->value());
	settings.setValue("ui/spinBox_training_deals",ui->spinBox_training_deals->value());
	settings.setValue("ui/spinBox_training_times",ui->spinBox_training_times->value());
	settings.setValue("ui/spinBox_trainning_trunk",ui->spinBox_trainning_trunk->value());
	settings.setValue("ui/checkBox_firstTrainning",ui->checkBox_firstTrainning->isChecked()?1:0);

}
void DialogNN::loadIni()
{
	QSettings settings(QCoreApplication::applicationFilePath()+".ini",QSettings::IniFormat);
	QString lineEdit_projpath = settings.value("ui/lineEdit_projpath",ui->lineEdit_projpath->text()).toString();
	QString lineEdit_NNPath = settings.value("ui/lineEdit_NNPath",QCoreApplication::applicationDirPath()).toString();
	QString lineEdit_features = settings.value("ui/lineEdit_features",ui->lineEdit_features->text()).toString();
	//current Project default
	QString lineEdit_layers = settings.value("ui/lineEdit_layers",ui->lineEdit_layers->text()).toString();
	int spinBox_inputs =  settings.value("ui/spinBox_inputs",ui->spinBox_inputs->value()).toInt();
	int spinBox_outputs =  settings.value("ui/spinBox_outputs",ui->spinBox_outputs->value()).toInt();;
	int spinBox_regular_base =  settings.value("ui/spinBox_regular_base",ui->spinBox_regular_base->value()).toInt();;
	int spinBox_regular_exp =  settings.value("ui/spinBox_regular_exp",ui->spinBox_regular_exp->value()).toInt();;
	int spinBox_training_deals =  settings.value("ui/spinBox_training_deals",ui->spinBox_training_deals->value()).toInt();;
	int spinBox_training_times =  settings.value("ui/spinBox_training_times",ui->spinBox_training_times->value()).toInt();;
	int spinBox_trainning_trunk =  settings.value("ui/spinBox_trainning_trunk",ui->spinBox_trainning_trunk->value()).toInt();;
	int checkBox_firstTrainning =  settings.value("ui/checkBox_firstTrainning",ui->checkBox_firstTrainning->isChecked()?1:0).toInt();
	//fill current Project default
	ui->lineEdit_layers->setText(lineEdit_layers);
	ui->lineEdit_NNPath->setText(lineEdit_NNPath);
	ui->spinBox_inputs->setValue(spinBox_inputs);
	ui->spinBox_outputs->setValue(spinBox_outputs);
	ui->spinBox_regular_base->setValue(spinBox_regular_base);
	ui->spinBox_regular_exp->setValue(spinBox_regular_exp);
	ui->spinBox_training_deals->setValue(spinBox_training_deals);
	ui->spinBox_training_times->setValue(spinBox_training_times);
	ui->spinBox_trainning_trunk->setValue(spinBox_trainning_trunk);
	ui->checkBox_firstTrainning->setChecked(checkBox_firstTrainning?true:false);
	//Fill ui, may cause project change signal
	ui->lineEdit_features->setText(lineEdit_features);
	ui->lineEdit_projpath->setText(lineEdit_projpath);
}
void DialogNN::readPrj()
{
	QString strPrjFile = ui->lineEdit_projpath->text();
	QSettings settings(strPrjFile,QSettings::IniFormat);
	//current Project default
	QString lineEdit_layers = settings.value("network/hidden_layer_size",ui->lineEdit_layers->text()).toString();
	int spinBox_inputs =  settings.value("network/input_nodes",ui->spinBox_inputs->value()).toInt();
	int spinBox_outputs =  settings.value("network/output_nodes",ui->spinBox_outputs->value()).toInt();
	//小数，指数换算
	double defaultl = ui->spinBox_regular_base->value() * pow(10, 0-ui->spinBox_regular_exp->value());
	double dReg =  settings.value("network/lambda",defaultl).toDouble();
	int spinBox_regular_base = 0 ,spinBox_regular_exp = 8;
	if (dReg>0)
	{
		double dZ = - log(dReg) / log (10) + 0.5;
		double dB = dReg * pow(10,int(dZ));
		spinBox_regular_exp =  int(dZ);
		spinBox_regular_base  = dB+0.5;
	}

	int spinBox_training_deals =  settings.value("performance/file_deal_times",ui->spinBox_training_deals->value()).toInt();
	int spinBox_training_times =  settings.value("performance/iterate_times",ui->spinBox_training_times->value()).toInt();
	int spinBox_trainning_trunk =  settings.value("performance/trunk",ui->spinBox_trainning_trunk->value()).toInt();
	//fill current Project default
	ui->lineEdit_layers->setText(lineEdit_layers);
	ui->spinBox_inputs->setValue(spinBox_inputs);
	ui->spinBox_outputs->setValue(spinBox_outputs);
	ui->spinBox_regular_base->setValue(spinBox_regular_base);
	ui->spinBox_regular_exp->setValue(spinBox_regular_exp);
	ui->spinBox_training_deals->setValue(spinBox_training_deals);
	ui->spinBox_training_times->setValue(spinBox_training_times);
	ui->spinBox_trainning_trunk->setValue(spinBox_trainning_trunk);

}
void DialogNN::savePrj()
{
	QString strPrjFile = ui->lineEdit_projpath->text();
	QSettings settings(strPrjFile,QSettings::IniFormat);
	//current Project default
	settings.setValue("network/hidden_layer_size",ui->lineEdit_layers->text());
	settings.setValue("network/input_nodes",ui->spinBox_inputs->value());
	settings.setValue("network/output_nodes",ui->spinBox_outputs->value());
	double defaultl = ui->spinBox_regular_base->value() * pow(10, 0-ui->spinBox_regular_exp->value());
	settings.setValue("network/lambda",defaultl);
	settings.setValue("performance/file_deal_times",ui->spinBox_training_deals->value());
	settings.setValue("performance/iterate_times",ui->spinBox_training_times->value());
	settings.setValue("performance/trunk",ui->spinBox_trainning_trunk->value());
	settings.setValue("performance/train_step",ui->spinBox_trainning_trunk->value()/2);
}

void DialogNN::on_lineEdit_projpath_textChanged(const QString &arg1)
{
	QFileInfo info(arg1);
	if (info.isFile())
	{
		ui->groupBox_paras->setEnabled(true);
		ui->pushButton_trainning->setEnabled(true);
		readPrj();
	}
	else
	{
		ui->groupBox_paras->setEnabled(false);
		ui->pushButton_trainning->setEnabled(false);
	}
}


void DialogNN::board_readyReadOutput()
{
	ui->plainTextEdit_log_out->appendPlainText(
				QString::fromStdString(m_processBoard->readAllStandardOutput().toStdString())
				);
}
void DialogNN::board_readyReadErr()
{
	ui->plainTextEdit_logerr->appendPlainText(
				QString::fromStdString(m_processBoard->readAllStandardError().toStdString())
				);
}
void DialogNN::board_finished(int code)
{
	board_readyReadOutput();
	board_readyReadErr();
	ui->plainTextEdit_log_out->appendPlainText(QString("\nprogram returns %1\n")
											  .arg(code)
											  );
}
void DialogNN::board_started()
{
	QUrl url("http://127.0.0.1:"+QString("%1").arg(m_nPort));
	QProcess process;
	QStringList args;
	args<<url.toString();
	process.start("firefox",args);
	process.waitForFinished();
}

